#include "Scene.h"

#include <QtGui/QGraphicsSceneMouseEvent>
#include <QtGui/QGraphicsItem>
#include <QtGui/QKeyEvent>

// Macro para verificar el movimiento de los elementos.
// #define __DEBUGP__

#ifdef __DEBUGP__
 #include <QDebug>
#endif

/**
  * \brief: Cuando la seleccion cambie, verificamos si tenemos algo seleccionado.
  *         Para asi activar o desactivar el boton para eliminar elementos.
  */
Scene::Scene(const QRectF &sceneRect, QObject *parent) :
    QGraphicsScene(sceneRect, parent), isCtrlActived(false) // En un inicio no esta activado
{
    QObject::connect(this, SIGNAL(selectionChanged()), this, SLOT(hasSelectedItem()));
}

/**
  * \brief: Detectamos si tenemos algun elemento seleccionado y
  *         emitimos una señal dependiendo de la seleccion afectando
  *         el estado del boton para eliminar elementos.
  */
void Scene::hasSelectedItem()
{
    if (this->selectedItems().isEmpty() == true)
        emit hasSelection(false);
    else
        emit hasSelection(true);
}

/**
  * \brief: Emitimos una señal donde se presiono doble cick para
  *         crear un elemento en esas coordenadas.
  */
void Scene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    emit mouseDoubleClick(event->scenePos());

    QGraphicsScene::mouseDoubleClickEvent(event);
}

/**
  * \brief: Obtiene las posiciones iniciales de los elementos
  *         seleccionados en ese momento.
  */
void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    // Limpiamos la seleccion privada para evitar acomulaciones.
    movingItems.clear();
    oldPos.clear();

    // Obtenemos el elemento que se encuentra en
    // las cordenadas donde se presiono el click izquierdo.
    QPointF mousePos(event->buttonDownScenePos(Qt::LeftButton).x(),
                     event->buttonDownScenePos(Qt::LeftButton).y());
    QGraphicsItem *item = itemAt(mousePos);

    // Si existe un elemento en esas coordenadas.
    if (item != 0) {
        /*
          Codigo A: Se encarga de añadir mas de un elemento a la seleccion privada (movingItems).

                    Primero verifica que tengamos algo en la seleccion pprivada (this->selectedItems()),
                    indicando asi que, hay uno o varios elementos A, B, C seleccionados completamente (press / release)
                    y hay otro elemento P seleccionado incompletamente (press).

                    Luego verifica que P, sea igual a A, B o C. Añadiendo la seleccion pprivada a la seleccion publica
                    puesto que el usuario desea mover la seleccion publica.
                    En caso de que P no sea igual a A, B o C, verifica que la tecla control este activada, de esta manera
                    aseguramos que el usuario desea añadir un nuevo elemento. Pero, si la tecla control no esta activada
                    quiere decir que el usuario desea hacer una nueva seleccion, por lo tanto no se añade la seleccion pprivada.

                    - Cabe aclarar que la seleccion pprivada, es la que nos retorna this->selectedItems(), y no la
                      seleccionada en los graficos (seleccion publica). Es un error creer que como en el caso 1, el
                      elemento A no es de la seleccion pprivada, puesto que no esta en la seleccion publica.
          */
        if (this->selectedItems().isEmpty() == false)
        {
            if (this->selectedItems().contains(item) == true ||
                    isCtrlActived == true) {
                movingItems = this->selectedItems();
            }
        }

        /*
          Codigo B: Se encarga de añadir el elemento P encima de todos.

                    Como en el caso en el que P sea igual a A, B o C (en el codigo A), A, B y C se añadieron a la seleccion
                    privada, pero como P es igual a uno de estos, quiere decir que ya esta en la seleccion privada y ya no se
                    necesita añadir de nuevo, por lo tanto se verifica si ya existe, si no, lo añade a la seleccion privada.
          */
        if (movingItems.contains(item) == false) {
            movingItems.push_back(item);
        }

        // Almacenamos las posiciones de los elementos, para utilizarla luego en el undo.
        foreach (QGraphicsItem *iItem, movingItems) {
            oldPos.push_back(iItem->pos());
        }
    }
    else {
        // Si no existe ningun elemento en esas coordenadas
        // significa que presiono (press) en un area en blanco,
        // por lo tanto eliminamos la seleccion privada.
        movingItems.clear();
        oldPos.clear();
    }

#ifdef __DEBUGP__
    qDebug() << "En Press";
    qDebug() << "\tSeleccion publica: Ver diagramas!";
    qDebug() << "\tSeleccion pprivada: " << this->selectedItems();
    qDebug() << "\tSeleccion privada: " << movingItems;
    qDebug() << "\tElemento press: " << item;
    qDebug() << "\tPosiciones iniciales: " << oldPos;
#endif

    QGraphicsScene::mousePressEvent(event);
}

/**
  * \brief: Al liberar el 'press' del mouse, si las coordenadas
  *         de los elementos de la seleccion cambiaron, se emite
  *         una señal con los elementos y sus coordenadas iniciales.
  */
void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    // Verifica que al liberar el mouse tengamos seleccion, para evitar
    // los releases en areas en blanco.
    if (movingItems.isEmpty() == false) {
        // Verificamos que las coordenadas cambiaron. Si solo una coordenada
        // cambio, significa que todas las demas tambien.
        if (oldPos.first() != this->movingItems.first()->pos()) {
            emit itemMoved(this->selectedItems(), oldPos);
#ifdef __DEBUGP__
            qDebug() << "\t\tMOVING_SIGNAL_EMITED";
#endif
        }
    }

#ifdef __DEBUGP__
    qDebug() << "En Release";
    qDebug() << "\tSeleccion publica: Ver diagramas!";
    qDebug() << "\tSeleccion pprivada: " << this->selectedItems();
    qDebug() << "\tSeleccion privada: " << movingItems;
    qDebug() << "\tElemento press: En release no hay!";
    qDebug() << "\tPosiciones iniciales: " << oldPos;
#endif

    QGraphicsScene::mouseReleaseEvent(event);
}

/**
  * \brief: Activamos una bandera como true puesto que la tecla control se presiono.
  *         Para saber cuando se selecciona mas de un elemento.
  */
void Scene::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Control)
        isCtrlActived = true;

    QGraphicsScene::keyPressEvent(event);
}

/**
  * \brief: Activamos una bandera como false puesto que la tecla control se libero.
  */
void Scene::keyReleaseEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Control)
        isCtrlActived = false;

    QGraphicsScene::keyReleaseEvent(event);
}
